#include "iomanager.h"
#include "macro.h"
#include "log.h"
#include <sys/epoll.h>
#include <fcntl.h>
#include <cstring>
#include <cerrno>

namespace yuechuan {
    static Logger::ptr g_logger = YUECHUAN_LOG_NAME("root");

    IOManager::FdContext::EventContext &IOManager::FdContext::getContext(IOManager::Event event) {
        switch (event) {
            case IOManager::READ:
                return read;
            case IOManager::WRITE:
                return write;

            default:
                YUECHUAN_ASSERT2(false, "getContext");
        }
    }

    void IOManager::FdContext::resetContext(EventContext &ctx) {
        ctx.scheduler = nullptr;
        ctx.fiber.reset();
        ctx.cb = nullptr;
    }

    void IOManager::FdContext::triggerEvent(Event event) {
        YUECHUAN_ASSERT(events & event);
        events = (Event) (events & ~event);
        EventContext &ctx = getContext(event);
        if (ctx.cb) {
            ctx.scheduler->schedule(&ctx.cb);
        } else {
            ctx.scheduler->schedule(&ctx.fiber);
        }
        ctx.scheduler = nullptr;

    }

    void IOManager::contextResize(size_t size) {
        m_fdContexts.resize(size);
        for (size_t i = 0; i < m_fdContexts.size(); ++i) {
            if (!m_fdContexts[i]) {
                m_fdContexts[i] = new FdContext;
                m_fdContexts[i]->fd = i;
            }
        }
    }

    IOManager::IOManager(size_t count, bool use_caller, const std::string &name)
            : Scheduler(count, use_caller, name) {
        contextResize(1);
        m_epfd = epoll_create(1);                      // 创建epoll，红黑树
        YUECHUAN_ASSERT(m_epfd > 0);

        int rt = pipe(m_tickleFds);       // 创建管道，传入的m_tickleFds用于返回管道两端的文件描述符[0]表示管道读端
        // [1]表示管道写入端,写入端写入数据到缓冲区，直到读取端从缓冲区读取
        YUECHUAN_ASSERT(rt == 0);

        epoll_event event;
        memset(&event, 0, sizeof(epoll_event));                                // 创建一个epoll_event对象并初始化

        event.events = EPOLLIN | EPOLLET;                              // 设置epoll事件模式为读和边缘触发，一次事件只会通知一次

        event.data.fd = m_tickleFds[0];                                        // 设置事件为触发事件列表中第一个也就是读事件

        rt = fcntl(m_tickleFds[0], F_SETFL, O_NONBLOCK);                    //	设置非阻塞
        YUECHUAN_ASSERT(!rt);
        // 参数( epoll根节点句柄， 操作， 在句柄操作， 操作的事件)
        rt = epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_tickleFds[0], &event);        // 添加对句柄队列中的事件执行对应的操作
        YUECHUAN_ASSERT(!rt);
        contextResize(32);                                         // 初始化等待执行事件上下文队列, Scheduler中的start方法
        start();

    }

    IOManager::~IOManager() {
        stop();
        close(m_epfd);
        close(m_tickleFds[0]);
        close(m_tickleFds[1]);

        for (size_t i = 0; i < m_fdContexts.size(); ++i) {
            if (m_fdContexts[i]) {
                delete m_fdContexts[i];
            }
        }
        YUECHUAN_LOG_INFO(g_logger) << " ~IOManager";
    }

    int IOManager::addEvent(int fd, Event event, std::function<void()> cb) {
        FdContext *fd_ctx = nullptr;
//        YUECHUAN_LOG_INFO(g_logger) << "m_fdContexts.size() = " << this->m_fdContexts.size();
        RWMutexType::ReadLock lock(m_mutex);
        if ((int) m_fdContexts.size() > fd) {
            fd_ctx = m_fdContexts[fd];
            lock.unlock();
        } else {
            lock.unlock();
            RWMutexType::WriteLock lock2(m_mutex);
            contextResize(fd * 1.5);
            fd_ctx = m_fdContexts[fd];
        }

        FdContext::MutexType::Lock lock2(fd_ctx->mutex);
        /// 判断文件句柄上下文中绑定的事件中是否有当前事件，有则报错
        if (fd_ctx->events & event) {
            YUECHUAN_LOG_ERROR(g_logger) << "addEvent  assert fd = " << fd
                                         << "event = " << event
                                         << "fd_ctx.event = " << fd_ctx->events;
            YUECHUAN_ASSERT(!(fd_ctx->events & event));
        }
        /// 判断是否绑定了事件，有则epoll_ctl中op要设置为MOD，否则设置为ADD
        int op = fd_ctx->events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
        epoll_event epollEvent;
        epollEvent.events = EPOLLET | fd_ctx->events | event;
        epollEvent.data.ptr = fd_ctx;                                       // 传递参数，表明该事件的句柄上下文是什么

        int rt = epoll_ctl(m_epfd, op, fd, &epollEvent);                    // 向根句柄注册事件
        if (rt) {
            YUECHUAN_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ","
                                         << op << ". " << fd << " , " << epollEvent.events
                                         << "):" << rt << " {" << errno << "} {" << strerror(errno) << "};";
            return -1;
        }
        ++m_pendingEventCount;                                            // 待执行的事件数要自增
        fd_ctx->events = (Event) (fd_ctx->events | event);                // 句柄上下文绑定的事件要和函数传递的事件进行或操作，来做到加和
        FdContext::EventContext &event_ctx = fd_ctx->getContext(event);   // 获取当前的事件上下文，对其进行设置
        YUECHUAN_ASSERT(!event_ctx.scheduler && !event_ctx.fiber && !event_ctx.cb);
        event_ctx.scheduler = Scheduler::GetThis();                        // 对于事件的协程调度器设置为当前的协程调度器
        if (cb) {
            event_ctx.cb.swap(cb);
        } else {
            event_ctx.fiber = Fiber::GetThis();
            YUECHUAN_ASSERT(event_ctx.fiber->getState() == Fiber::EXEC);
        }
        return 0;
    }

    bool IOManager::delEvent(int fd, Event event) {
        RWMutexType::ReadLock lock(m_mutex);
        if ((int) m_fdContexts.size() <= fd) {
            return false;
        }
        FdContext *fd_ctx = m_fdContexts[fd];
        lock.unlock();

        FdContext::MutexType::Lock lock2(fd_ctx->mutex);
        /// 要确保监听的文件句柄上下文绑定的事件中有要删除的事件
        if (!(fd_ctx->events & event)) {
            return false;
        }

        Event new_events = (Event) (fd_ctx->events & ~event);                // &~将event从fd_ctx->events中去除
        int op = new_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;                // 当为空时，直接将该文件句柄从红黑树中删除
        epoll_event epollEvent;
        epollEvent.events = EPOLLET | new_events;                                // 边缘触发
        epollEvent.data.ptr = fd_ctx;                                            // 传递参数，表明事件对应的句柄上下文

        int rt = epoll_ctl(m_epfd, op, fd, &epollEvent);
        if (rt) {
            YUECHUAN_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ","
                                         << op << ". " << fd << " , " << epollEvent.events
                                         << "):" << rt << " {" << errno << "} {" << strerror(errno) << "};";
            return false;
        }
        --m_pendingEventCount;                                              // 待执行的事件数要自减
        fd_ctx->events = new_events;
        FdContext::EventContext &event_ctx = fd_ctx->getContext(event);
        fd_ctx->resetContext(event_ctx);

        return true;
    }

    bool IOManager::cancelEvent(int fd, Event event) {
        RWMutexType::ReadLock lock(m_mutex);
        if ((int) m_fdContexts.size() <= fd) {
            return false;
        }
        FdContext *fd_ctx = m_fdContexts[fd];
        lock.unlock();

        FdContext::MutexType::Lock lock2(fd_ctx->mutex);
        if (!(fd_ctx->events & event)) {
            return false;
        }

        auto new_events = (Event) (fd_ctx->events & ~event);
        int op = new_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
        epoll_event epollEvent{};
        epollEvent.events = EPOLLET | new_events;
        epollEvent.data.ptr = fd_ctx;

        int rt = epoll_ctl(m_epfd, op, fd, &epollEvent);
        if (rt) {
            YUECHUAN_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ","
                                         << op << ". " << fd << " , " << epollEvent.events
                                         << "):" << rt << " {" << errno << "} {" << strerror(errno) << "};";
            return false;
        }
        fd_ctx->triggerEvent(event);                                  // 唤醒文件句柄对象绑定的event事件
        --m_pendingEventCount;                                        // 强制执行了对应的事件，待执行的事件数要自减
        return true;

    }

    bool IOManager::cancelAll(int fd) {
        RWMutexType::ReadLock lock(m_mutex);
        if ((int) m_fdContexts.size() <= fd) {
            return false;
        }
        FdContext *fd_ctx = m_fdContexts[fd];
        lock.unlock();

        FdContext::MutexType::Lock lock2(fd_ctx->mutex);
        /// 这里仅仅需要判断是否有事件即可
        if (!fd_ctx->events) {
            return false;
        }

        int op = EPOLL_CTL_DEL;
        epoll_event epollEvent;
        epollEvent.events = 0;
        epollEvent.data.ptr = fd_ctx;

        int rt = epoll_ctl(m_epfd, op, fd, &epollEvent);
        if (rt) {
            YUECHUAN_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ","
                                         << op << ". " << fd << " , " << epollEvent.events
                                         << "):" << rt << " {" << errno << "} {" << strerror(errno) << "};";
            return false;
        }
        if (fd_ctx->events & READ) {
            fd_ctx->triggerEvent(READ);
            --m_pendingEventCount;
        }
        if (fd_ctx->events & WRITE) {
            fd_ctx->triggerEvent(WRITE);
            --m_pendingEventCount;
        }
        YUECHUAN_ASSERT(fd_ctx->events == 0);
        return true;
    }

    IOManager *IOManager::GetThis() {
        return dynamic_cast<IOManager *>(Scheduler::GetThis());
    }

    bool IOManager::stopping() {
        uint64_t timeout = 0;
        return stopping(timeout);
    }

    bool IOManager::stopping(uint64_t &timeout) {
        timeout = getNextTimer();
        return timeout == ~0ul && m_pendingEventCount == 0 && Scheduler::stopping();
    }

    void IOManager::idle() {
        YUECHUAN_LOG_INFO(g_logger) << "idle";
        const uint64_t MAX_EVENTS = 256;
        epoll_event *events = new epoll_event[MAX_EVENTS]();
        std::shared_ptr<epoll_event> shared_events(events, [](epoll_event *ptr) { delete[] ptr; });
        //shared_ptr( Y* ptr, Deleter d ); shared_ptr构造函数可以传递一个删除器，我们的要求就是当前事件的只能指针被释放后，其后面所有的事件指针都被释放
        while (true) {
            uint64_t next_timeout = 0;
            if (stopping(next_timeout)) {
                    YUECHUAN_LOG_INFO(g_logger) << "name = " << getName() << " idle stopping exit";
                    break;

            }
            int rt = 0;
            do {
                static const int MAX_TIMEOUT = 100;
                if(next_timeout != ~0ul){
                    next_timeout = (int)next_timeout > MAX_TIMEOUT ? MAX_TIMEOUT: next_timeout ;
                }else{
                    next_timeout = MAX_TIMEOUT;
                }
                rt = epoll_wait(m_epfd, events, MAX_EVENTS, (int)next_timeout);
                if (rt < 0 && errno == EINTR) {            // 当就绪队列中不为空时，跳出循环
                } else {
                    break;
                }
            } while (true);

            std::vector<std::function<void()> > cbs;
            listExpiredCb(cbs);
            if(!cbs.empty()){
                schedule(cbs.begin(), cbs.end());
                cbs.clear();
            }
            for (int i = 0; i < rt; ++i) {
                epoll_event &event = events[i];        // 取得就绪队列中的事件
                if (event.data.fd == m_tickleFds[0]) {// m_tickleFds[0]是IN事件文件表示符，读事件，外部有事件发送消息给当前句柄
                    uint8_t dummy;
                    while (read(m_tickleFds[0], &dummy, 1) == 1);
                    continue;
                }

                /// 事件传递的参数是通过ptr来进行的，当初我们在绑定红黑树时传递了文件句柄的上下文作为参数
                FdContext *fd_ctx = (FdContext *) event.data.ptr;
                FdContext::MutexType::Lock lock(fd_ctx->mutex);
                /// 当事件出现错误，或者挂断时，要获取句柄绑定的事件是读还是写
                if (event.events & (EPOLLERR | EPOLLHUP)) {
                    event.events |= (EPOLLIN | EPOLLOUT) & fd_ctx->events;
                }
                int real_events = NONE;
                if (event.events & EPOLLIN) {
                    real_events |= READ;
                }
                if (event.events & EPOLLOUT) {
                    real_events |= WRITE;
                }

                if ((fd_ctx->events & real_events) == NONE) {
                    continue;
                }
                /// 将其绑定的事件去除了读或者写事件外句柄绑定的事件
                int left_events = (fd_ctx->events & ~real_events);
                /// 去除了读写事件后剩余的事件，当为空时，epoll_ctl就要执行删除，点那个不为空时要进行修改
                int op = left_events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
                event.events = EPOLLET | left_events;
                int rt2 = epoll_ctl(m_epfd, op, fd_ctx->fd, &event);
                if (rt2) {
                    YUECHUAN_LOG_ERROR(g_logger) << "epoll_ctl(" << m_epfd << ","
                                                 << op << ". " << fd_ctx->fd << " , " << event.events
                                                 << "):" << rt2 << " {" << errno << "} {" << strerror(errno) << "};";
                    continue;
                }
                if (real_events & READ) {
                    fd_ctx->triggerEvent(READ);
                    --m_pendingEventCount;
                }
                if (real_events & WRITE) {
                    fd_ctx->triggerEvent(WRITE);
                    --m_pendingEventCount;
                }
            }

            Fiber::ptr cur = Fiber::GetThis();
            Fiber *raw_ptr = cur.get();
            cur.reset();

            raw_ptr->swapOut();
        }
    }

    void IOManager::tickle() {
        if (hasIdleCount()) {
            int rt = write(m_tickleFds[1], "T", 1);
            YUECHUAN_ASSERT(rt == 1);
        }

    }

    void IOManager::onTimerInsertForFront() {
        tickle();
    }

} // namespace yuechuan
